﻿using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

// ReSharper disable once CheckNamespace
namespace WeihanLi.Extensions
{
    // ReSharper disable once InconsistentNaming
    public static class ILGeneratorExtensions
    {
        public static readonly MethodInfo GetMethodFromHandle = ExpressionExtension.GetMethod<Func<RuntimeMethodHandle, RuntimeTypeHandle, MethodBase>>((h1, h2) => MethodBase.GetMethodFromHandle(h1, h2));

        public static void EmitMethod(this ILGenerator ilGenerator, MethodInfo method)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }
            EmitMethod(ilGenerator, method, method.DeclaringType);
        }

        public static void EmitMethod(this ILGenerator ilGenerator, MethodInfo method, Type declaringType)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }
            if (declaringType == null)
            {
                throw new ArgumentNullException(nameof(declaringType));
            }

            ilGenerator.Emit(OpCodes.Ldtoken, method);
            ilGenerator.Emit(OpCodes.Ldtoken, declaringType);
            ilGenerator.Emit(OpCodes.Call, GetMethodFromHandle);
            ilGenerator.EmitConvertToType(typeof(MethodBase), typeof(MethodInfo));
        }

        public static void EmitConvertToObject(this ILGenerator ilGenerator, Type typeFrom)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (typeFrom == null)
            {
                throw new ArgumentNullException(nameof(typeFrom));
            }

            if (typeFrom.IsGenericParameter)
            {
                ilGenerator.Emit(OpCodes.Box, typeFrom);
            }
            else
            {
                ilGenerator.EmitConvertToType(typeFrom, typeof(object), true);
            }
        }

        public static void EmitConvertFromObject(this ILGenerator ilGenerator, Type typeTo)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (typeTo == null)
            {
                throw new ArgumentNullException(nameof(typeTo));
            }

            if (typeTo.IsGenericParameter)
            {
                ilGenerator.Emit(OpCodes.Unbox_Any, typeTo);
            }
            else
            {
                ilGenerator.EmitConvertToType(typeof(object), typeTo, true);
            }
        }

        public static void EmitConvertToType(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked = true)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (typeFrom == null)
            {
                throw new ArgumentNullException(nameof(typeFrom));
            }
            if (typeTo == null)
            {
                throw new ArgumentNullException(nameof(typeTo));
            }

            var nnExprType = typeFrom.Unwrap();
            var nnType = typeTo.Unwrap();

            if (nnType == nnExprType || nnType.IsEquivalentTo(nnExprType))
            {
                return;
            }

            if (typeFrom.IsInterface || // interface cast
              typeTo.IsInterface ||
               typeFrom == typeof(object) || // boxing cast
               typeTo == typeof(object) ||
               typeFrom == typeof(Enum) ||
               typeFrom == typeof(ValueType) ||
               TypeInfoUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo))
            {
                ilGenerator.EmitCastToType(typeFrom, typeTo);
            }
            else if (typeFrom.IsNullableType() || typeTo.IsNullableType())
            {
                ilGenerator.EmitNullableConversion(typeFrom, typeTo, isChecked);
            }
            else if (!(typeFrom.IsConvertible() && typeTo.IsConvertible()) // primitive runtime conversion
                     &&
                     (nnExprType.GetTypeInfo().IsAssignableFrom(nnType) || // down cast
                     nnType.GetTypeInfo().IsAssignableFrom(nnExprType))) // up cast
            {
                ilGenerator.EmitCastToType(typeFrom, typeTo);
            }
            else if (typeFrom.IsArray && typeTo.IsArray)
            {
                // See DevDiv Bugs #94657.
                ilGenerator.EmitCastToType(typeFrom, typeTo);
            }
            else
            {
                ilGenerator.EmitNumericConversion(typeFrom, typeTo, isChecked);
            }
        }

        private static void EmitNullableConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)
        {
            bool isTypeFromNullable = typeFrom.IsNullableType();
            bool isTypeToNullable = typeTo.IsNullableType();
            if (isTypeFromNullable && isTypeToNullable)
                ilGenerator.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked);
            else if (isTypeFromNullable)
                ilGenerator.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked);
            else
                ilGenerator.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);
        }

        private static void EmitNullableToNullableConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)
        {
            Label labIfNull = default(Label);
            Label labEnd = default(Label);
            LocalBuilder locFrom = null;
            LocalBuilder locTo = null;
            locFrom = ilGenerator.DeclareLocal(typeFrom);
            ilGenerator.Emit(OpCodes.Stloc, locFrom);
            locTo = ilGenerator.DeclareLocal(typeTo);
            // test for null
            ilGenerator.Emit(OpCodes.Ldloca, locFrom);
            ilGenerator.EmitHasValue(typeFrom);
            labIfNull = ilGenerator.DefineLabel();
            ilGenerator.Emit(OpCodes.Brfalse_S, labIfNull);
            ilGenerator.Emit(OpCodes.Ldloca, locFrom);
            ilGenerator.EmitGetValueOrDefault(typeFrom);
            Type nnTypeFrom = TypeInfoUtils.GetNonNullableType(typeFrom);
            Type nnTypeTo = TypeInfoUtils.GetNonNullableType(typeTo);
            ilGenerator.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked);
            // construct result type
            ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
            ilGenerator.Emit(OpCodes.Newobj, ci);
            ilGenerator.Emit(OpCodes.Stloc, locTo);
            labEnd = ilGenerator.DefineLabel();
            ilGenerator.Emit(OpCodes.Br_S, labEnd);
            // if null then create a default one
            ilGenerator.MarkLabel(labIfNull);
            ilGenerator.Emit(OpCodes.Ldloca, locTo);
            ilGenerator.Emit(OpCodes.Initobj, typeTo);
            ilGenerator.MarkLabel(labEnd);
            ilGenerator.Emit(OpCodes.Ldloc, locTo);
        }

        private static void EmitNullableToNonNullableConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)
        {
            if (typeTo.IsValueType)
                ilGenerator.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked);
            else
                ilGenerator.EmitNullableToReferenceConversion(typeFrom);
        }

        private static void EmitNullableToNonNullableStructConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)
        {
            LocalBuilder locFrom = null;
            locFrom = ilGenerator.DeclareLocal(typeFrom);
            ilGenerator.Emit(OpCodes.Stloc, locFrom);
            ilGenerator.Emit(OpCodes.Ldloca, locFrom);
            ilGenerator.EmitGetValue(typeFrom);
            Type nnTypeFrom = TypeInfoUtils.GetNonNullableType(typeFrom);
            ilGenerator.EmitConvertToType(nnTypeFrom, typeTo, isChecked);
        }

        private static void EmitNullableToReferenceConversion(this ILGenerator ilGenerator, Type typeFrom)
        {
            // We've got a conversion from nullable to Object, ValueType, Enum, etc.  Just box it so that
            // we get the nullable semantics.
            ilGenerator.Emit(OpCodes.Box, typeFrom);
        }

        private static void EmitNonNullableToNullableConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)
        {
            LocalBuilder locTo = ilGenerator.DeclareLocal(typeTo);
            Type nnTypeTo = typeTo.Unwrap();
            ilGenerator.EmitConvertToType(typeFrom, nnTypeTo, isChecked);
            ConstructorInfo ci = typeTo.GetConstructor(new[] { nnTypeTo });
            ilGenerator.Emit(OpCodes.Newobj, ci);
            ilGenerator.Emit(OpCodes.Stloc, locTo);
            ilGenerator.Emit(OpCodes.Ldloc, locTo);
        }

        private static void EmitNumericConversion(this ILGenerator ilGenerator, Type typeFrom, Type typeTo, bool isChecked)
        {
            bool isFromUnsigned = TypeInfoUtils.IsUnsigned(typeFrom);
            bool isFromFloatingPoint = TypeInfoUtils.IsFloatingPoint(typeFrom);
            if (typeTo == typeof(float))
            {
                if (isFromUnsigned)
                    ilGenerator.Emit(OpCodes.Conv_R_Un);
                ilGenerator.Emit(OpCodes.Conv_R4);
            }
            else if (typeTo == typeof(double))
            {
                if (isFromUnsigned)
                    ilGenerator.Emit(OpCodes.Conv_R_Un);
                ilGenerator.Emit(OpCodes.Conv_R8);
            }
            else
            {
                TypeCode tc = typeTo.GetTypeCode();
                if (isChecked)
                {
                    // Overflow checking needs to know if the source value on the IL stack is unsigned or not.
                    if (isFromUnsigned)
                    {
                        switch (tc)
                        {
                            case TypeCode.SByte:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I1_Un);
                                break;

                            case TypeCode.Int16:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I2_Un);
                                break;

                            case TypeCode.Int32:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I4_Un);
                                break;

                            case TypeCode.Int64:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I8_Un);
                                break;

                            case TypeCode.Byte:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U1_Un);
                                break;

                            case TypeCode.UInt16:
                            case TypeCode.Char:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U2_Un);
                                break;

                            case TypeCode.UInt32:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U4_Un);
                                break;

                            case TypeCode.UInt64:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U8_Un);
                                break;

                            default:
                                throw new InvalidCastException();
                        }
                    }
                    else
                    {
                        switch (tc)
                        {
                            case TypeCode.SByte:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I1);
                                break;

                            case TypeCode.Int16:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I2);
                                break;

                            case TypeCode.Int32:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I4);
                                break;

                            case TypeCode.Int64:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_I8);
                                break;

                            case TypeCode.Byte:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U1);
                                break;

                            case TypeCode.UInt16:
                            case TypeCode.Char:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U2);
                                break;

                            case TypeCode.UInt32:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U4);
                                break;

                            case TypeCode.UInt64:
                                ilGenerator.Emit(OpCodes.Conv_Ovf_U8);
                                break;

                            default:
                                throw new InvalidCastException();
                        }
                    }
                }
                else
                {
                    switch (tc)
                    {
                        case TypeCode.SByte:
                            ilGenerator.Emit(OpCodes.Conv_I1);
                            break;

                        case TypeCode.Byte:
                            ilGenerator.Emit(OpCodes.Conv_U1);
                            break;

                        case TypeCode.Int16:
                            ilGenerator.Emit(OpCodes.Conv_I2);
                            break;

                        case TypeCode.UInt16:
                        case TypeCode.Char:
                            ilGenerator.Emit(OpCodes.Conv_U2);
                            break;

                        case TypeCode.Int32:
                            ilGenerator.Emit(OpCodes.Conv_I4);
                            break;

                        case TypeCode.UInt32:
                            ilGenerator.Emit(OpCodes.Conv_U4);
                            break;

                        case TypeCode.Int64:
                            if (isFromUnsigned)
                            {
                                ilGenerator.Emit(OpCodes.Conv_U8);
                            }
                            else
                            {
                                ilGenerator.Emit(OpCodes.Conv_I8);
                            }
                            break;

                        case TypeCode.UInt64:
                            if (isFromUnsigned || isFromFloatingPoint)
                            {
                                ilGenerator.Emit(OpCodes.Conv_U8);
                            }
                            else
                            {
                                ilGenerator.Emit(OpCodes.Conv_I8);
                            }
                            break;

                        default:
                            throw new InvalidCastException();
                    }
                }
            }
        }

        public static void EmitCastToType(this ILGenerator ilGenerator, Type typeFrom, Type typeTo)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (!typeFrom.IsValueType && typeTo.IsValueType)
            {
                ilGenerator.Emit(OpCodes.Unbox_Any, typeTo);
            }
            else if (typeFrom.IsValueType && !typeTo.IsValueType)
            {
                ilGenerator.Emit(OpCodes.Box, typeFrom);
                if (typeTo != typeof(object))
                {
                    ilGenerator.Emit(OpCodes.Castclass, typeTo);
                }
            }
            else if (!typeFrom.IsValueType && !typeTo.IsValueType)
            {
                ilGenerator.Emit(OpCodes.Castclass, typeTo);
            }
            else
            {
                throw new InvalidCastException($"Can not cast {typeFrom} to {typeTo}.");
            }
        }

        public static LocalBuilder DeclareReturnValue(this ILGenerator il, Type returnType)
        {
            if (returnType != typeof(void))
            {
                return il.DeclareLocal(returnType);
            }

            return null;
        }

        public static void Return(this ILGenerator il, MethodInfo method, LocalBuilder local)
        {
            if (method.ReturnType != typeof(void))
            {
                il.Emit(OpCodes.Stloc, local);
                il.Emit(OpCodes.Ldloc, local);
            }

            il.Emit(OpCodes.Ret);
        }

        public static void Call(this ILGenerator il, MethodInfo method)
        {
            il.Emit(OpCodes.Call, method);
        }

        public static void Call(this ILGenerator il, ConstructorInfo constructor)
        {
            il.Emit(OpCodes.Call, constructor);
        }

        public static void New(this ILGenerator il, ConstructorInfo constructor)
        {
            il.Emit(OpCodes.Newobj, constructor);
        }

        // https://stackoverflow.com/a/48149905/5519747
        public static void LoadObj(this ILGenerator il, object obj)
        {
            if (null == obj)
            {
                il.EmitNull();
                return;
            }

            var type = obj.GetType();

            var gch = GCHandle.Alloc(obj);
            var ptr = GCHandle.ToIntPtr(gch);

            if (IntPtr.Size == 4)
                il.Emit(OpCodes.Ldc_I4, ptr.ToInt32());
            else
                il.Emit(OpCodes.Ldc_I8, ptr.ToInt64());

            il.Emit(OpCodes.Ldobj, type);
        }

        public static void EmitThis(this ILGenerator ilGenerator)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }

            ilGenerator.Emit(OpCodes.Ldarg_0);
        }

        public static void EmitLoadArg(this ILGenerator ilGenerator, int index)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }

            switch (index)
            {
                case 0:
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    break;

                case 1:
                    ilGenerator.Emit(OpCodes.Ldarg_1);
                    break;

                case 2:
                    ilGenerator.Emit(OpCodes.Ldarg_2);
                    break;

                case 3:
                    ilGenerator.Emit(OpCodes.Ldarg_3);
                    break;

                default:
                    if (index <= byte.MaxValue)
                    {
                        ilGenerator.Emit(OpCodes.Ldarg_S, (byte)index);
                    }
                    else
                    {
                        ilGenerator.Emit(OpCodes.Ldarg, index);
                    }
                    break;
            }
        }

        public static void EmitNull(this ILGenerator ilGenerator)
        {
            ilGenerator.Emit(OpCodes.Ldnull);
        }

        public static void EmitString(this ILGenerator ilGenerator, string value)
        {
            ilGenerator.Emit(OpCodes.Ldstr, value);
        }

        public static void EmitBoolean(this ILGenerator ilGenerator, bool value)
        {
            if (value)
            {
                ilGenerator.Emit(OpCodes.Ldc_I4_1);
            }
            else
            {
                ilGenerator.Emit(OpCodes.Ldc_I4_0);
            }
        }

        public static void EmitHasValue(this ILGenerator ilGenerator, Type nullableType)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            MethodInfo mi = nullableType.GetTypeInfo().GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
            ilGenerator.Emit(OpCodes.Call, mi);
        }

        public static void EmitGetValueOrDefault(this ILGenerator ilGenerator, Type nullableType)
        {
            MethodInfo mi = nullableType.GetTypeInfo().GetMethod("GetValueOrDefault", Type.EmptyTypes);
            ilGenerator.Emit(OpCodes.Call, mi);
        }

        public static void EmitGetValue(this ILGenerator ilGenerator, Type nullableType)
        {
            MethodInfo mi = nullableType.GetTypeInfo().GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
            ilGenerator.Emit(OpCodes.Call, mi);
        }

        public static void EmitDefaultValue(this ILGenerator ilGenerator, Type type)
        {
            if (ilGenerator == null)
            {
                throw new ArgumentNullException(nameof(ilGenerator));
            }
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Object:
                case TypeCode.DateTime:
                    if (type.IsValueType)
                    {
                        // Type.GetTypeCode on an enum returns the underlying
                        // integer TypeCode, so we won't get here.
                        // This is the IL for default(T) if T is a generic type
                        // parameter, so it should work for any type. It's also
                        // the standard pattern for structs.
                        LocalBuilder lb = ilGenerator.DeclareLocal(type);
                        ilGenerator.Emit(OpCodes.Ldloca, lb);
                        ilGenerator.Emit(OpCodes.Initobj, type);
                        ilGenerator.Emit(OpCodes.Ldloc, lb);
                    }
                    else
                    {
                        ilGenerator.Emit(OpCodes.Ldnull);
                    }
                    break;

                case TypeCode.Empty:
                case TypeCode.String:
                    ilGenerator.Emit(OpCodes.Ldnull);
                    break;

                case TypeCode.Boolean:
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    ilGenerator.Emit(OpCodes.Ldc_I4_0);
                    break;

                case TypeCode.Int64:
                case TypeCode.UInt64:
                    ilGenerator.Emit(OpCodes.Ldc_I4_0);
                    ilGenerator.Emit(OpCodes.Conv_I8);
                    break;

                case TypeCode.Single:
                    ilGenerator.Emit(OpCodes.Ldc_R4, default(float));
                    break;

                case TypeCode.Double:
                    ilGenerator.Emit(OpCodes.Ldc_R8, default(double));
                    break;

                case TypeCode.Decimal:
                    ilGenerator.Emit(OpCodes.Ldc_I4_0);
                    ilGenerator.Emit(OpCodes.Newobj, typeof(decimal).GetTypeInfo().GetConstructor(new Type[] { typeof(int) }));
                    break;

                default:
                    throw new InvalidOperationException("Code supposed to be unreachable.");
            }
        }
    }

    internal static class TypeInfoUtils
    {
        internal static bool AreEquivalent(Type t1, Type t2)
        {
            return t1 == t2 || t1.IsEquivalentTo(t2);
        }

        internal static Type GetNonNullableType(this Type type)
        {
            if (type.IsNullableType())
            {
                return type.GetGenericArguments()[0];
            }
            return type;
        }

        internal static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)
        {
            if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)
                return false;

            var genericDelegate = source.GetGenericTypeDefinition();

            if (dest.GetGenericTypeDefinition() != genericDelegate)
                return false;

            var genericParameters = genericDelegate.GetTypeInfo().GetGenericArguments();
            var sourceArguments = source.GetGenericArguments();
            var destArguments = dest.GetGenericArguments();

            for (var iParam = 0; iParam < genericParameters.Length; ++iParam)
            {
                var sourceArgument = sourceArguments[iParam].GetTypeInfo();
                var destArgument = destArguments[iParam].GetTypeInfo();

                if (AreEquivalent(sourceArgument, destArgument))
                {
                    continue;
                }

                var genericParameter = genericParameters[iParam].GetTypeInfo();

                if (IsInvariant(genericParameter))
                {
                    return false;
                }

                if (IsCovariant(genericParameter))
                {
                    if (!HasReferenceConversion(sourceArgument, destArgument))
                    {
                        return false;
                    }
                }
                else if (IsContravariant(genericParameter))
                {
                    if (sourceArgument.IsValueType || destArgument.IsValueType)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        private static bool IsDelegate(Type t)
        {
            return t.IsSubclassOf(typeof(System.MulticastDelegate));
        }

        private static bool IsInvariant(Type t)
        {
            return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);
        }

        private static bool IsCovariant(this Type t)
        {
            return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);
        }

        internal static bool HasReferenceConversion(Type source, Type dest)
        {
            // void -> void conversion is handled elsewhere
            // (it's an identity conversion)
            // All other void conversions are disallowed.
            if (source == typeof(void) || dest == typeof(void))
            {
                return false;
            }

            var nnSourceType = TypeInfoUtils.GetNonNullableType(source).GetTypeInfo();
            var nnDestType = TypeInfoUtils.GetNonNullableType(dest).GetTypeInfo();

            // Down conversion
            if (nnSourceType.IsAssignableFrom(nnDestType))
            {
                return true;
            }
            // Up conversion
            if (nnDestType.IsAssignableFrom(nnSourceType))
            {
                return true;
            }
            // Interface conversion
            if (source.IsInterface || dest.IsInterface)
            {
                return true;
            }
            // Variant delegate conversion
            if (IsLegalExplicitVariantDelegateConversion(source, dest))
                return true;

            // Object conversion
            if (source == typeof(object) || dest == typeof(object))
            {
                return true;
            }
            return false;
        }

        private static bool IsContravariant(Type t)
        {
            return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);
        }

        internal static bool IsConvertible(this Type type)
        {
            var notNullType = type.Unwrap();
            if (notNullType.IsEnum)
            {
                return true;
            }
            switch (Type.GetTypeCode(notNullType))
            {
                case TypeCode.Boolean:
                case TypeCode.Byte:
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Char:
                    return true;

                default:
                    return false;
            }
        }

        internal static bool IsUnsigned(Type type)
        {
            switch (Type.GetTypeCode(type.Unwrap()))
            {
                case TypeCode.Byte:
                case TypeCode.UInt16:
                case TypeCode.Char:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    return true;

                default:
                    return false;
            }
        }

        internal static bool IsFloatingPoint(Type type)
        {
            switch (Type.GetTypeCode(type.Unwrap()))
            {
                case TypeCode.Single:
                case TypeCode.Double:
                    return true;

                default:
                    return false;
            }
        }
    }
}
